
#ifndef IEnumerable_h
#define IEnumerable_h

#include <CSharpGarbagePointer.h>

// Riccsson includes.
#include <System/Collections/IEnumerator.h>
#include <System/NotImplementedException.h>
#include <System/Object.h>

namespace Riccsson
{
	namespace System
	{
		namespace Collections
		{
			class enumerable_iterator
			{
			private:
				IEnumerator* p;
				bool m_finished;
			public:

				//------------------------------------------------------------------------------
				// Constructor.
				enumerable_iterator(IEnumerator* x, bool isEndIterator = false)
					: p(x)
					, m_finished(isEndIterator)
				{
				}

				//------------------------------------------------------------------------------
				// Copy constructor.
				enumerable_iterator(const enumerable_iterator& mit)
					: p(mit.p)
					, m_finished(mit.m_finished)
				{
				}

				//------------------------------------------------------------------------------
				enumerable_iterator& operator++()
				{
					bool movedToNext( p->MoveNext() );

					// If it not moved to next. Then the index was at the end and the foreach loop is finished.
					if(!movedToNext)
					{
						m_finished = true;
					}

					return *this;
				}

				//------------------------------------------------------------------------------
				enumerable_iterator operator++(int)
				{
					enumerable_iterator tmp(*this);
					operator++();
					return tmp;
				}

				//------------------------------------------------------------------------------
				bool operator==(const enumerable_iterator& rhs) const
				{
					return m_finished == rhs.m_finished;
				}

				//------------------------------------------------------------------------------
				bool operator!=(const enumerable_iterator& rhs) const
				{
					bool temp = m_finished != rhs.m_finished;
					if(!temp)
					{
						delete p;
					}
					return temp;
				}

				//------------------------------------------------------------------------------
				GC_PTR<object> operator*() const
				{
					return p->Current.get();
				}
			};

			class enumerable_const_iterator 
				//: public std::iterator< std::input_iterator_tag, GC_PTR<object> >
			{
			private:
				IEnumerator* p;
				bool m_finished;
			public:

				//------------------------------------------------------------------------------
				// Constructor.
				enumerable_const_iterator(IEnumerator* x, bool isEndIterator = false)
					: p(x)
					, m_finished(isEndIterator)
				{
				}

				//------------------------------------------------------------------------------
				// Copy constructor.
				enumerable_const_iterator(const enumerable_const_iterator& mit)
					: p(mit.p)
					, m_finished(mit.m_finished)
				{
				}

				//------------------------------------------------------------------------------
				enumerable_const_iterator& operator++()
				{
					bool movedToNext( p->MoveNext() );

					// If it not moved to next. Then the index was at the end and the foreach loop is finished.
					if(!movedToNext)
					{
						m_finished = true;
					}

					return *this;
				}

				//------------------------------------------------------------------------------
				enumerable_const_iterator operator++(int)
				{
					enumerable_const_iterator tmp(*this);
					operator++();
					return tmp;
				}

				//------------------------------------------------------------------------------
				bool operator==(const enumerable_const_iterator& rhs) const
				{
					bool temp = m_finished == rhs.m_finished;
					return temp;
				}

				//------------------------------------------------------------------------------
				bool operator!=(const enumerable_const_iterator& rhs) const
				{
					bool temp = m_finished != rhs.m_finished;
					if(!temp)
					{
						delete rhs.p;
					}
					return temp;
				}

				//------------------------------------------------------------------------------
				GC_PTR<object> operator*() const
				{
					GC_PTR<object> reference( p->Current );
					return reference;
					//return p->Current;
				}
			};

			// Summary:
			//     Exposes the enumerator, which supports a simple iteration over a collection
			//     of a specified type.
			//
			// Type parameters:
			//   T:
			//     The type of objects to enumerate.This type parameter is covariant. That is,
			//     you can use either the type you specified or any type that is more derived.
			//     For more information about covariance and contravariance, see Covariance
			//     and Contravariance in Generics.
			interface IEnumerable //<out T> : IEnumerable
			{
				// Summary:
				//     Returns an enumerator that iterates through the collection.
				//
				// Returns:
				//     A System.Collections.Generic.IEnumerator that can be used to iterate through
				//     the collection.
				public: virtual IEnumerator* GetEnumerator() = 0;
				public: virtual IEnumerator* GetEnumeratorConst() const { throw NotImplementedException("Subclasses of IEnumerable must override GetEnumeratorConst, to be able to loop over constant variables."); }

			public:

				typedef enumerable_iterator iterator;
				typedef enumerable_const_iterator const_iterator;

				//------------------------------------------------------------------------------
				iterator begin()
				{
					iterator iterator( GetEnumerator() );

					return iterator;
				}
				const_iterator begin() const
				{
					const_iterator iterator( GetEnumeratorConst() );

					return iterator;
				}

				//------------------------------------------------------------------------------
				const iterator cbegin() const
				{
					IEnumerator* enumerator( 0 );
					iterator iterator( enumerator, true );

					return iterator;
				}

				//------------------------------------------------------------------------------
				iterator end()
				{
					IEnumerator* enumerator( 0 );
					iterator iterator( enumerator, true );

					return iterator;
				}

				//------------------------------------------------------------------------------
				const_iterator end() const
				{
					const_iterator iterator( GetEnumeratorConst(), true );

					return iterator;
				}

				//------------------------------------------------------------------------------
				const iterator cend() const
				{
					iterator iterator( GetEnumeratorConst(), true );

					return iterator;
				}
			};
		}
	}
}

#endif // IENUMERABLE_H